package com.jfinal.core;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

import javax.servlet.ServletContext;

import com.jfinal.config.Constants;
import com.jfinal.config.JFinalConfig;
import com.jfinal.handler.Handler;
import com.jfinal.handler.HandlerFactory;
import com.jfinal.spring.AbstractSpringJFinal;
import com.jfinal.util.ReflectUtils;

/**
 * JFinal 适配器(主要原因是JFinal作用于太小了)
 * 使用 adapter 必须在 SpringJFinalFilter 之后
 * @ClassName: JFinalAdapter 
 * @author huangx
 * @date 2015年2月6日 上午9:05:26
 */
public class JFinalAdapter extends AbstractSpringJFinal {
	private final JFinal jfinal;
	// adapter
	private ConfigAdapter configAdapter = ConfigAdapter.on();
	// jfinal Field
	private static final Field handlerField = ReflectUtils.field(JFinal.class, "handler");
	private static final Field actionMappingField = ReflectUtils.field(JFinal.class, "actionMapping");
	private static final Field servletContextField = ReflectUtils.field(JFinal.class, "servletContext");
	private static final Field contextPathField = ReflectUtils.field(JFinal.class, "contextPath");
	private static final Field constantsField = ReflectUtils.field(JFinal.class, "constants");
	// jfinal Method
	private static final Method initPathUtilMethod = ReflectUtils.method(JFinal.class, "initPathUtil");
	private static final Method initActionMappingMethod = ReflectUtils.method(JFinal.class, "initActionMapping");
	private static final Method initRenderMethod = ReflectUtils.method(JFinal.class, "initRender");
	private static final Method initOreillyCosMethod = ReflectUtils.method(JFinal.class, "initOreillyCos");
	private static final Method initI18nMethod = ReflectUtils.method(JFinal.class, "initI18n");
	private static final Method initTokenManagerMethod = ReflectUtils.method(JFinal.class, "initTokenManager");
	// 
	private ActionMapping actionMapping;
	private Handler handler;
	private Constants constants;

	private JFinalAdapter(JFinal jfinal) {
		this.jfinal = jfinal;
	}

	// bind
	public static JFinalAdapter on() {
		return new JFinalAdapter(JFinal.me());
	}

	/*------------------------------------------------------------------*/
	public boolean init(JFinalConfig jfinalConfig, ServletContext servletContext) {
		this.setServletContext(servletContext);
		this.setContextPath(servletContext.getContextPath());

		ReflectUtils.invoke(jfinal, initPathUtilMethod);
		Config.configJFinal(jfinalConfig); // start plugin and init logger factory in this method
		this.setConstants(configAdapter.getConstants());

		ReflectUtils.invoke(jfinal, initActionMappingMethod);
		this.initHandler();
		ReflectUtils.invoke(jfinal, initRenderMethod);
		ReflectUtils.invoke(jfinal, initOreillyCosMethod);
		ReflectUtils.invoke(jfinal, initI18nMethod);
		ReflectUtils.invoke(jfinal, initTokenManagerMethod);

		return true;
	}

	protected void initHandler() {
		Handler actionHandler = new SpringHandler(this.getActionMapping(), this.getConstants(), super.getApplicationContext());
		this.setHandler(HandlerFactory.getHandler(Config.getHandlers().getHandlerList(), actionHandler));
	}

	/*--------------------------- attr ----------------------------------*/
	public ActionMapping getActionMapping() {
		if (actionMapping == null) actionMapping = ReflectUtils.get(jfinal, actionMappingField);
		return actionMapping;
	}

	public Handler getHandler() {
		if (handler == null) handler = jfinal.getHandler();
		return handler;
	}

	public Constants getConstants() {
		if (constants == null) constants = configAdapter.getConstants();
		return constants;
	}

	public JFinalAdapter setHandler(Handler handler) {
		ReflectUtils.set(jfinal, handlerField, this.handler = handler);
		return this;
	}

	public JFinalAdapter setServletContext(ServletContext servletContext) {
		ReflectUtils.set(jfinal, servletContextField, servletContext);
		return this;
	}

	public JFinalAdapter setContextPath(String contextPath) {
		ReflectUtils.set(jfinal, contextPathField, contextPath);
		return this;
	}

	public JFinalAdapter setConstants(Constants constants) {
		ReflectUtils.set(jfinal, constantsField, this.constants = constants);
		return this;
	}

}
